import { SQLiteDatabase, SQLError, ResultSet } from './wcdb';
// eslint-disable-next-line import/extensions
import wcdb from './wcdb/wcdb.js';
import Log from './log';

const TAG = 'Database';

wcdb.enablePromise(true);

export interface RowData {
  [key: string]: any;
}

export interface DataBaseProps {
  dbName: string;
  key?: string | null;
}

class Database {
  private dbConn!: SQLiteDatabase;

  public close(): Promise<void> {
    return new Promise((resolve, reject): void => {
      wcdb.close(
        (): void => {
          resolve();
        },
        (err: SQLError): void => {
          reject(err);
        }
      );
    });
  }

  public delete({ dbName }: DataBaseProps): Promise<void> {
    return new Promise((resolve, reject): void => {
      wcdb.deleteDatabase(
        dbName,
        (): void => {
          resolve();
        },
        (err: SQLError): void => {
          reject(err);
        }
      );
    });
  }

  public connect({ dbName, key = null }: DataBaseProps): Promise<void> {
    return new Promise((resolve, reject): void => {
      wcdb.openDatabase(
        { name: dbName, location: 'default', key },
        (db: SQLiteDatabase): void => {
          this.dbConn = db;
          resolve();
        },
        (err: SQLError): void => {
          Log.e(TAG, JSON.stringify({ err }));
          reject(err);
        }
      );
    });
  }

  public createTable(sql: string): Promise<void> {
    return new Promise((resolve, reject): void => {
      this.dbConn.transaction(
        (): void => {
          this.dbConn
            .executeSql(sql, [])
            .then((): void => {
              resolve();
            })
            .catch((err: SQLError): void => {
              reject(err);
            });
        },
        (err: SQLError): void => {
          reject(err);
        },
        (): void => {
          // fallback & wait for resolve/reject of executeSql
        }
      );
    });
  }

  // 插入一行, 返回insertId (支持auto increment)
  public insertOne(table: string, data: RowData): Promise<number> {
    return new Promise((resolve, reject): void => {
      const sql = `INSERT INTO ${table} (${Object.keys(data).join(
        ','
      )}) VALUES (${Array(Object.keys(data).length)
        .fill('?')
        .join(',')})`;

      this.dbConn.transaction(
        (): void => {
          this.dbConn
            .executeSql(sql, Object.values(data))
            .then((results: [ResultSet]): void => {
              const r = results[0];
              resolve(r && r.insertId);
            })
            .catch((err: SQLError): void => {
              Log.e(TAG, err.message);
              reject(err);
            });
        },
        (err: SQLError): void => {
          reject(err);
        },
        (): void => {
          // fallback & wait for resolve/reject of executeSql
        }
      );
    });
  }

  // query所有行 返回对象数组
  public query(sql: string): Promise<RowData[]> {
    return new Promise((resolve, reject): void => {
      this.dbConn.transaction(
        (): void => {
          this.dbConn
            .executeSql(sql, [])
            .then((results: [ResultSet]): void => {
              if (results[0] === undefined) {
                resolve(undefined);
                return;
              }

              const datas = [];
              for (let i = 0; i < results[0].rows.length; i += 1) {
                datas.push(results[0].rows.item(i));
              }
              resolve(datas);
            })
            .catch((err: SQLError): void => {
              reject(err);
            });
        },
        (err: SQLError): void => {
          reject(err);
        },
        (): void => {
          // fallback & wait for resolve/reject of executeSql
        }
      );
    });
  }

  // query只取首行 返回单个对象
  public queryOne(sql: string): Promise<RowData> {
    const that = this;
    return new Promise((resolve, reject): void => {
      that
        .query(sql)
        .then((datas: RowData[]): void => {
          if (datas === undefined || datas.length === 0) {
            resolve(undefined);
            return;
          }
          resolve(datas[0]);
        })
        .catch((err: SQLError): void => {
          reject(err);
        });
    });
  }

  // replace
  public replace(table: string, data: RowData): Promise<number> {
    return new Promise((resolve, reject): void => {
      const sql = `REPLACE INTO ${table} (${Object.keys(data).join(
        ','
      )}) VALUES (${Array(Object.keys(data).length)
        .fill('?')
        .join(',')})`;

      this.dbConn.transaction(
        (): void => {
          this.dbConn
            .executeSql(sql, Object.values(data))
            .then((results: [ResultSet]): void => {
              resolve(results[0].rowsAffected);
            })
            .catch((err: SQLError): void => {
              reject(err);
            });
        },
        (err: SQLError): void => {
          reject(err);
        },
        (): void => {
          // fallback & wait for resolve/reject of executeSql
        }
      );
    });
  }

  // 直接执行操作, 常用于update/delete
  public execute(sql: string): Promise<number> {
    return new Promise((resolve, reject): void => {
      this.dbConn.transaction(
        (): void => {
          this.dbConn
            .executeSql(sql, [])
            .then((results: [ResultSet]): void => {
              resolve(results[0].rowsAffected);
            })
            .catch((err: SQLError): void => {
              reject(err);
            });
        },
        (err: SQLError): void => {
          reject(err);
        },
        (): void => {
          // fallback & wait for resolve/reject of executeSql
        }
      );
    });
  }
}

export default Database;
